//
// Created by Administrator on 2021/1/31 0031.
//

#include "JqhFFmpeg.h"

JqhFFmpeg::JqhFFmpeg(JqhPlayStatus *playStatus, CallJava *callJava, const char *url) {
    this->callJava = callJava;
    this->url = url;
    this->playStatus = playStatus;
    pthread_mutex_init(&init_mutex, NULL);
    pthread_mutex_init(&seek_mutex, NULL);

}

JqhFFmpeg::~JqhFFmpeg() {
    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&seek_mutex);
}


void *decodeFFmpeg(void *data) {
    JqhFFmpeg *fFmpeg = (JqhFFmpeg *)(data);
    fFmpeg->decodeFFmpegThread();
    pthread_exit(&fFmpeg->decodeThread);
}


void JqhFFmpeg::parpared() {
    pthread_create(&decodeThread, NULL, decodeFFmpeg, this);
}

int avformat_callback(void *ctx) {
    JqhFFmpeg *fFmpeg = (JqhFFmpeg *)(ctx);
    if (fFmpeg->playStatus->exit) {
        return AVERROR_EOF;
    }
    return 0;
}

// 在线程中被调用
void JqhFFmpeg::decodeFFmpegThread() {
    pthread_mutex_lock(&init_mutex);
    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();

    pFormatCtx->interrupt_callback.callback = avformat_callback;
    pFormatCtx->interrupt_callback.opaque = this;
    if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
        if (LOG_DEBUG) {
            LOGE("can not open url --- :%s", url);
        }
        callJava->onCallError(CHILD_THREAD, 1001, "can not open url");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        if (LOG_DEBUG) {
            LOGE("can not find streams from url:%s", url);
        }
        callJava->onCallError(CHILD_THREAD, 1002, "can not find streams from url");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }
    for (int i = 0 ; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            if (audio == NULL) {
                audio = new JqhAudio(playStatus, pFormatCtx->streams[i]->codecpar->sample_rate, callJava);
                audio->streamIndex = i;
            }
            audio->streamIndex = i;
            audio->codecpar = pFormatCtx->streams[i]->codecpar;
            audio->duration = pFormatCtx->duration / AV_TIME_BASE;
            audio->time_base = pFormatCtx->streams[i]->time_base;
            duration = audio->duration;
        }
    }

    AVCodec *dec = avcodec_find_decoder(audio->codecpar->codec_id);
    if (!dec) {
        if (LOG_DEBUG) {
            LOGE("can not find decoder")
        }
        callJava->onCallError(CHILD_THREAD, 1003, "can not find decoder");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    audio->avCodecContext = avcodec_alloc_context3(dec);

    if (!audio->avCodecContext) {
        if (LOG_DEBUG) {
            LOGE("can not alloc new decoderctx")
        }
        callJava->onCallError(CHILD_THREAD, 1004, "can not alloc new decoderctx");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    // 把解码器属性复制到context中
    if(avcodec_parameters_to_context(audio->avCodecContext, audio->codecpar) < 0) {
        if (LOG_DEBUG) {
            LOGE("can not fill decoderctx")
        }
        callJava->onCallError(CHILD_THREAD, 1005, "can not fill decoderctx");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    if (avcodec_open2(audio->avCodecContext, dec, 0) != 0 ) {
        if (LOG_DEBUG) {
            LOGE("can not open audio streams")
        }
        callJava->onCallError(CHILD_THREAD, 1006, "can not find decoder");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    // 初始化准备完毕，回调到java层
    callJava->onCallParpared(CHILD_THREAD);

    pthread_mutex_unlock(&init_mutex);


}

void JqhFFmpeg::start() {
    if (audio == NULL) {
        if (LOG_DEBUG) {
            LOGE("audio is null")
        }
        callJava->onCallError(CHILD_THREAD, 1007, "audio is null");
        return;
    }
    // 播放，开启解码线程
    audio->play();
    int count = 0;
    while(playStatus != NULL && !playStatus->exit) {

        if (playStatus->seek) {
//            av_usleep(1000 * 100);
            continue;
        }
        if (audio->queue->getQueueSize() > 100) {
            // 避免缓存过大，方便seek
//            av_usleep(1000 * 100);
            continue;
        }
        AVPacket *avPacket = av_packet_alloc();

        pthread_mutex_lock(&seek_mutex);
        int ret = av_read_frame(pFormatCtx, avPacket);
        pthread_mutex_unlock(&seek_mutex);
        if ( ret == 0) {
            if (avPacket->stream_index == audio->streamIndex) {
                count++;
//                if (LOG_DEBUG) {
//                    LOGD("解码第%d帧", count);
//                }
                audio->queue->putAVPacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
            }
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            // 把缓存的播放完
            while (playStatus != NULL && !playStatus->exit) {
                if (audio->queue->getQueueSize() > 0) {
//                    av_usleep(1000 * 100);
                    continue;
                } else {
                    playStatus->exit = true;
                    break;
                }
            }
            break;
        }
    }

//    while (audio->queue->getQueueSize() > 0) {
//        AVPacket *avPacket = av_packet_alloc();
//        audio->queue->getAVPacket(avPacket);
//        av_packet_free(&avPacket);
//        av_free(avPacket);
//        avPacket = NULL;
//    }

    if (callJava != NULL) callJava->onCallComplete(CHILD_THREAD);
    exit = true;
    if (LOG_DEBUG) {
        LOGD("解码完成");
    }
}

void JqhFFmpeg::pause() {
    if (audio != NULL) audio->pause();
}

void JqhFFmpeg::resume() {
    if (audio != NULL) audio->resume();
}

void JqhFFmpeg::release() {

//    if (playStatus->exit) {
//        return;
//    }
    playStatus->exit = true;

    pthread_mutex_lock(&init_mutex);
    int sleepCount = 0 ;
    while (!exit) {
        if (sleepCount > 1000) {
            exit = true;
        }
        if (LOG_DEBUG) {
            LOGE("wait ffmpeg exit %d", sleepCount);
        }
        sleepCount++;
//        av_usleep(1000 * 10);// 10毫秒
    }

    if(audio != NULL) {
        audio->release();
        delete(audio);
        audio = NULL;
    }

    if (pFormatCtx != NULL) {
        avformat_close_input(&pFormatCtx);
        avformat_free_context(pFormatCtx);
        pFormatCtx = NULL;

    }

    playStatus = NULL;
    callJava = NULL;
    pthread_mutex_unlock(&init_mutex);
}

void JqhFFmpeg::seek(int64_t secds) {
    if (duration <= 0) return ;
    if (secds >= 0 && secds <= duration) {
        if(audio != NULL) {
            playStatus->seek = true;
            audio->queue->clearAvpacket();
            audio->clock = 0;
            audio->last_time = 0;
            pthread_mutex_lock(&seek_mutex);
            int64_t  rel = secds * AV_TIME_BASE;
            avcodec_flush_buffers(audio->avCodecContext); //  防止seek还会残留播放seek之前的bug
            avformat_seek_file(pFormatCtx, -1, INT64_MIN, rel, INT64_MAX, 0);
            pthread_mutex_unlock(&seek_mutex);
            playStatus->seek = false;
        }
    }
}

void JqhFFmpeg::setVolume(int percent) {
    if (audio != NULL) {
        audio->setVolume(percent);
    }
}

void JqhFFmpeg::setMute(int mute) {
    if (audio != NULL) {
        audio->setMute(mute);
    }
}

void JqhFFmpeg::setPitch(float pitch) {
    if (audio != NULL) {
        audio->setPitch(pitch);
    }
}

void JqhFFmpeg::setSpeed(float speed) {
    if (audio != NULL) {
        audio->setSpeed(speed);
    }
}

int JqhFFmpeg::getSampleRate() {
    if (audio != NULL) {
        return audio->avCodecContext->sample_rate;
    }
    return 0;
}

void JqhFFmpeg::startStopRecord(bool start) {
    if (audio != NULL) {
        return audio->startStopRecord(start);
    }
}
